﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Windows;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using Bymed.SpineScan.WPF.Model;
using Bymed.SpineScan.WPF.Shared;
using Bymed.SpineScan.Service;
using Bymed.SpineScan.Service.Shared;
using Bymed.SpineScan.DataModel;
using Bymed.RegistrationCodeManager.Service.Shared;

namespace Bymed.SpineScan.WPF.ViewModel
{
    /// <summary>
    /// This class contains properties that a View can data bind to.
    /// <para>
    /// See http://www.galasoft.ch/mvvm
    /// </para>
    /// </summary>
    public class DeviceConfigurationViewModel : MyViewModelBase
    {
        private DeviceConfigurationService _deviceConfigurationService;

        /// <summary>
        /// Initializes a new instance of the DeviceConfigurationViewModel class.
        /// </summary>
        public DeviceConfigurationViewModel(IDataService dataService)
            : base(dataService)
        {
            _deviceConfigurationService = ServiceFactory.CreateDeviceConfigurationService();
        }

        public override void ResetProperties()
        {
            try
            {
                base.ResetProperties();

                GenerateSerialPortNameList();
                SetProperties(_deviceConfigurationService.Load());
            }
            catch (Exception ex)
            {
                RequestDisplayErrorMessage(ex.Message);
            }
        }

        /// <summary>
        /// The <see cref="SerialPortNameList" /> property's name.
        /// </summary>
        public const string SerialPortNameListPropertyName = "SerialPortNameList";

        private ObservableCollection<string> _serialPortNameList = new ObservableCollection<string>();

        /// <summary>
        /// Sets and gets the SerialPortNameList property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public ObservableCollection<string> SerialPortNameList
        {
            get
            {
                return _serialPortNameList;
            }

            set
            {
                if (_serialPortNameList == value)
                {
                    return;
                }

                RaisePropertyChanging(SerialPortNameListPropertyName);
                _serialPortNameList = value;
                RaisePropertyChanged(SerialPortNameListPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="SerialPortName" /> property's name.
        /// </summary>
        public const string SerialPortNamePropertyName = "SerialPortName";

        private string _serialPortName = string.Empty;

        /// <summary>
        /// Sets and gets the SerialPortName property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string SerialPortName
        {
            get
            {
                return _serialPortName;
            }

            set
            {
                if (_serialPortName == value)
                {
                    return;
                }

                RaisePropertyChanging(SerialPortNamePropertyName);
                _serialPortName = value;
                RaisePropertyChanged(SerialPortNamePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="ComputerNumber" /> property's name.
        /// </summary>
        public const string ComputerNumberPropertyName = "ComputerNumber";

        private string _computerNumber = string.Empty;

        /// <summary>
        /// Sets and gets the ComputerNumber property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string ComputerNumber
        {
            get
            {
                return _computerNumber;
            }

            set
            {
                if (_computerNumber == value)
                {
                    return;
                }

                RaisePropertyChanging(ComputerNumberPropertyName);
                _computerNumber = value;
                RaisePropertyChanged(ComputerNumberPropertyName);
            }
        }

        /// <summary>
        /// The <see cref="RegistrationCode" /> property's name.
        /// </summary>
        public const string RegistrationCodePropertyName = "RegistrationCode";

        private string _registrationCode = string.Empty;

        /// <summary>
        /// Sets and gets the RegistrationCode property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string RegistrationCode
        {
            get
            {
                return _registrationCode;
            }

            set
            {
                if (_registrationCode == value)
                {
                    return;
                }

                RaisePropertyChanging(RegistrationCodePropertyName);
                _registrationCode = value;
                RaisePropertyChanged(RegistrationCodePropertyName);
            }
        }

        /// <summary>
        /// The <see cref="AvailableAuthorisedUsage" /> property's name.
        /// </summary>
        public const string AvailableAuthorisedUsagePropertyName = "AvailableAuthorisedUsage";

        private string _availableAuthorisedUsage = string.Empty;

        /// <summary>
        /// Sets and gets the AvailableAuthorisedUsage property.
        /// Changes to that property's value raise the PropertyChanged event. 
        /// </summary>
        public string AvailableAuthorisedUsage
        {
            get
            {
                return _availableAuthorisedUsage;
            }

            set
            {
                if (_availableAuthorisedUsage == value)
                {
                    return;
                }

                RaisePropertyChanging(AvailableAuthorisedUsagePropertyName);
                _availableAuthorisedUsage = value;
                RaisePropertyChanged(AvailableAuthorisedUsagePropertyName);
            }
        }

        private RelayCommand _saveCommand;

        /// <summary>
        /// Gets the SaveCommand.
        /// </summary>
        public RelayCommand SaveCommand
        {
            get
            {
                return _saveCommand ?? (_saveCommand = new RelayCommand(
                    ExecuteSaveCommand,
                    CanExecuteSaveCommand));
            }
        }

        private bool CanExecuteSaveCommand()
        {
            var canSave = true;

            if (string.IsNullOrEmpty(RegistrationCode.Trim()) || 
                RegistrationCodeManagerUtility.DecryptRegistrationCode(RegistrationCode) == null)
            {
                canSave = false;
            }

            return canSave;
        }

        private void ExecuteSaveCommand()
        {
            try
            {
                Save();

                // Send RequestRestartCurrentApplicationMessage to WindowBase
                Messenger.Default.Send<int>(0, MyViewModelBase.RequestRestartCurrentApplicationMessageToken);
            }
            catch (Exception ex)
            {
                RequestDisplayErrorMessage(ex.Message);
            }
        }

        /// <summary>
        /// Save current device configuration
        /// </summary>
        private void Save()
        {
            var availableAuthorisedUsageCount = RegistrationCodeManagerUtility.DecryptRegistrationCode(RegistrationCode).AuthorisedUsageCount;

            var entity = new DeviceConfiguration
            {
                SerialPortName = SerialPortName,
                RegistrationCode = RegistrationCode,
                AvailableAuthorisedUsageCount = RegistrationCodeManagerUtility.EncryptString(availableAuthorisedUsageCount.ToString())
            };

            _deviceConfigurationService.Save(entity);
        }

        /// <summary>
        /// Generate the Serial port name list from COM1 to COM9.
        /// </summary>
        private void GenerateSerialPortNameList()
        {
            SerialPortNameList.Clear();

            for (int i = 1; i <= 9; i++)
            {
                SerialPortNameList.Add("COM" + i.ToString());
            }
        }

        /// <summary>
        /// Set current all properties based on provided instance of DeviceConfiguration.
        /// </summary>
        /// <param name="entity">The instance of DeviceConfiguration that contains all data.</param>
        private void SetProperties(DeviceConfiguration entity)
        {
            SerialPortName = entity.SerialPortName;
            ComputerNumber = entity.ComputerNumber;
            RegistrationCode = entity.RegistrationCode;

            SetAvailableAuthorisedUsage(entity.RegistrationCode, entity.AvailableAuthorisedUsageCount);
        }

        /// <summary>
        /// Set the AvailableAuthorisedUsage based on saved RegistrationCode and AvailableAuthorisedUsageCount.
        /// </summary>
        /// <param name="registrationCode">The saved and decrypted RegistrationCode.</param>
        /// <param name="availableAuthorisedUsageCount">The saved and decrypted AvailableAuthorisedUsageCount.</param>
        private void SetAvailableAuthorisedUsage(string registrationCode, string availableAuthorisedUsageCount)
        {
            var genuineCheckService = new GenuineCheckService();
            var currentDeviceSerialNumber = genuineCheckService.GetSerialNumber();
            var currentComputerName = genuineCheckService.GetComputerName();
            var decryptedRegistrationCode = RegistrationCodeManagerUtility.DecryptRegistrationCode(registrationCode);

            if (decryptedRegistrationCode == null)
            {
                AvailableAuthorisedUsage = "无效注册码，错误代码001。";
            }
            else if (currentComputerName != decryptedRegistrationCode.ComputerNumber)
            {
                AvailableAuthorisedUsage = "无效注册码，错误代码002。";
            }
            else if (currentDeviceSerialNumber != decryptedRegistrationCode.DeviceNumber)
            {
                AvailableAuthorisedUsage = "无效注册码，错误代码003。";
            }
            else if (availableAuthorisedUsageCount == "0")
            {
                AvailableAuthorisedUsage = "无效注册码，错误代码004。";
            }
            else if (availableAuthorisedUsageCount == "-1")
            {
                AvailableAuthorisedUsage = "无限制";
            }
            else
            {
                AvailableAuthorisedUsage = availableAuthorisedUsageCount;
            }
        }
    }
}